#
#   This file is part of Corrade.
#
#   Copyright © 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016,
#               2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025
#             Vladimír Vondruš <mosra@centrum.cz>
#   Copyright © 2023 Vincent Le Garrec <github@le-garrec.fr>
#
#   Permission is hereby granted, free of charge, to any person obtaining a
#   copy of this software and associated documentation files (the "Software"),
#   to deal in the Software without restriction, including without limitation
#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
#   and/or sell copies of the Software, and to permit persons to whom the
#   Software is furnished to do so, subject to the following conditions:
#
#   The above copyright notice and this permission notice shall be included
#   in all copies or substantial portions of the Software.
#
#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
#   DEALINGS IN THE SOFTWARE.
#

# Oldest known CMake version in use is 3.5 on ancient Ubuntu 16.04, all other
# distributions have a newer version. Additionally, CMake 3.27+ warns if
# anything older than 3.5 is used in cmake_minimum_required(). CMake 3.31+
# warns if anything older than 3.10 is used, so it's used as the policy max
# version.
#
# Next minimal version will probably need to be 3.6 for Android NDK
# compatibility.
cmake_minimum_required(VERSION 3.5...3.10)

# As reported in https://github.com/mosra/corrade/issues/52 and then tracked
# down to https://gitlab.kitware.com/cmake/cmake/issues/18099, CMake < 3.12 has
# various issues with the new XCode, resulting in weird build failures in
# Corrade. CMake 3.12 fixes that by temporarily switching to the legacy build
# system for Xcode 10. Instead of failing later during the build, fail upfront
# with a clear message.
if(APPLE AND CMAKE_GENERATOR STREQUAL Xcode AND NOT XCODE_VERSION VERSION_LESS 10.0 AND CMAKE_VERSION VERSION_LESS 3.12.0)
    message(FATAL_ERROR "CMake < 3.12 and Xcode 10 have various compatibility issues, breaking the Corrade build. Please upgrade to CMake 3.12 to use Xcode 10.")
endif()

# CMake 3.12+ uses the policy max version specified in
# cmake_minimum_required(), meaning that with ...3.10, everything until CMP0071
# gets set to NEW implicitly. We however want to keep compatibility with
# versions before 3.12, so the NEW policies are still being hand-picked. Also
# don't want to do a blanket cmake_policy(VERSION) because that may break
# behavior for existing projects that rely on the OLD behavior.

# Don't restrict INTERPROCEDURAL_OPTIMIZATION only for icc on Linux
if(POLICY CMP0069)
    cmake_policy(SET CMP0069 NEW)
endif()
# If CMAKE_AUTOMOC is set, all uses of corrade_add_resource() would otherwise
# complain on 3.10 that AUTOMOC is not processing GENERATED files
if(POLICY CMP0071)
    cmake_policy(SET CMP0071 NEW)
endif()
# Allow <PackageName>_ROOT to be used on 3.12+ to point to per-package install
# locations that find_package(PackageName) subsequently picks up
if(POLICY CMP0074)
    cmake_policy(SET CMP0074 NEW)
endif()
# Allow also <PACKAGENAME>_ROOT (i.e., uppercase), on 3.27+
if(POLICY CMP0144)
    cmake_policy(SET CMP0144 NEW)
endif()
# Superprojects can use just set(CORRADE_WITH_BLAH ON) without FORCE CACHE on
# 3.13+
if(POLICY CMP0077)
    cmake_policy(SET CMP0077 NEW)
endif()

project(Corrade CXX)

# Use folders for nice tree in Visual Studio and XCode
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/modules/" ${CMAKE_MODULE_PATH})

include(CMakeDependentOption)

# Options that used to be unprefixed. New options shouldn't be added to this
# list.
set(_CORRADE_DEPRECATED_UNPREFIXED_OPTIONS
    WITH_INTERCONNECT
    WITH_MAIN
    WITH_PLUGINMANAGER
    WITH_TESTSUITE
    WITH_UTILITY
    WITH_RC
    MSVC_COMPATIBILITY
    MSVC2017_COMPATIBILITY
    MSVC2015_COMPATIBILITY
    BUILD_DEPRECATED
    BUILD_MULTITHREADED
    BUILD_STATIC
    BUILD_STATIC_PIC
    BUILD_STATIC_UNIQUE_GLOBALS
    BUILD_TESTS
    TESTSUITE_TARGET_XCTEST
    UTILITY_USE_ANSI_COLORS)
# If during the first run (i.e., when the variable isn't in cache yet), check
# if any of the prefixed options are already set. If so, we assume the user is
# already switched to the prefixed options and won't accept the deprecated
# unprefixed options for backwards compatibility. This way it's possible for
# projects to reuse these variables for other purposes without affecting
# Corrade in any way.
if(NOT DEFINED _CORRADE_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS)
    set(_CORRADE_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS ON CACHE INTERNAL "")
    foreach(option ${_CORRADE_DEPRECATED_UNPREFIXED_OPTIONS})
        if(DEFINED CORRADE_${option})
            set(_CORRADE_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS OFF CACHE INTERNAL "")
            break()
        endif()
    endforeach()
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    # We want to present the option to the user, but we also want to implicitly
    # enable it during the first run, unless manually overriden. Thus we first
    # remember if it was defined, then define the option() and then below check
    # the remembered state. Also checking the deprecated unprefixed name for
    # backwards compatibility.
    if(NOT DEFINED CORRADE_MSVC_COMPATIBILITY AND NOT DEFINED MSVC_COMPATIBILITY)
        set(_CORRADE_MSVC_COMPATIBILITY_NOT_DEFINED ON)
    endif()
    option(CORRADE_MSVC_COMPATIBILITY "Enable compatibility mode for MSVC 2019+ without /permissive- set (might disable some features)" OFF)

    option(CORRADE_MSVC2017_COMPATIBILITY "Enable compatibility mode for MSVC 2017 (might disable some features)" OFF)
    option(CORRADE_MSVC2015_COMPATIBILITY "Enable compatibility mode for MSVC 2015 (might disable some features)" OFF)
endif()

# Attempt to find a native CORRADE_RC_EXECUTABLE when cross-compiling. Needs to
# be done before the CORRADE_WITH_RC option is defined, as that one can be
# turned off if a native one is found.
if(CMAKE_CROSSCOMPILING)
    find_program(CORRADE_RC_EXECUTABLE corrade-rc)
    mark_as_advanced(CORRADE_RC_EXECUTABLE)
endif()

# If CORRADE_RC_EXECUTABLE was found when cross-compiling or if it was passed
# explicitly to use an out-of-tree binary, create the Corrade::rc target for
# use by corrade_add_resource().
#
# src/Corrade/Utility/CMakeLists.txt sets CORRADE_RC_EXECUTABLE to Corrade::rc
# when a cross-compiled corrade-rc is used via an emulator to prevent the
# find_program() and related logic from being done again for each CMake run. If
# that's the case, we shouldn't attempt to create the Corrade::rc target here.
if(CORRADE_RC_EXECUTABLE AND NOT CORRADE_RC_EXECUTABLE STREQUAL Corrade::rc)
    # Strictly speaking the GLOBAL isn't needed, but when crosscompiling
    # with Corrade as a CMake subproject and
    #   find_package(Corrade REQUIRED rc) # or Utility
    # isn't present for whatever reason, corrade_add_resource() called
    # outside of Corrade itself would fail on the rc target not existing.
    # We could require everyone to call find_package() to fix this one
    # particular issue but that would be kinda mean. Don't be mean. The
    # find_package() call is still recommended as it unifies the external
    # and subproject workflows but it shouldn't be *required*.
    add_executable(Corrade::rc IMPORTED GLOBAL)
    set_property(TARGET Corrade::rc PROPERTY IMPORTED_LOCATION ${CORRADE_RC_EXECUTABLE})
# add_custom_command() uses CMAKE_CROSSCOMPILING_EMULATOR only since 3.6.
# It's unlikely that people would use ancient CMake 3.5 for crosscompiling
# so the message doesn't mention that, nevertheless check for the version
# to not make this silently fail much later at build time with 3.5.
elseif(CMAKE_CROSSCOMPILING AND (NOT CMAKE_CROSSCOMPILING_EMULATOR OR CMAKE_VERSION VERSION_LESS 3.6))
    message(FATAL_ERROR "Native corrade-rc executable, which is needed when crosscompiling without CMAKE_CROSSCOMPILING_EMULATOR set, was not found. Either build it, make it available through PATH or pass its location to CMake using the CORRADE_RC_EXECUTABLE option, or specify CMAKE_CROSSCOMPILING_EMULATOR to have Corrade run the cross-compiled executable instead.")
else()
    # Target Corrade::rc gets created in src/Corrade/Utility/CMakeLists.txt
endif()

# Option-independent platform discovery
if(CMAKE_SYSTEM_NAME STREQUAL Emscripten)
    set(CORRADE_TARGET_EMSCRIPTEN 1)
elseif(UNIX)
    # Both APPLE and UNIX are defined on OSX
    if(APPLE)
        set(CORRADE_TARGET_APPLE 1)

        if(CMAKE_OSX_SYSROOT MATCHES "/iPhoneOS[0-9.]*\\.sdk")
            set(CORRADE_TARGET_IOS 1)
        elseif(CMAKE_OSX_SYSROOT MATCHES "/iPhoneSimulator[0-9.]*\\.sdk")
            set(CORRADE_TARGET_IOS 1)
            set(CORRADE_TARGET_IOS_SIMULATOR 1)
        endif()
    endif()

    # UNIX is also defined on Android
    if(CMAKE_SYSTEM_NAME STREQUAL Android)
        set(CORRADE_TARGET_ANDROID 1)
    endif()

    # Emscripten is Unix too, this selects only the other ones
    set(CORRADE_TARGET_UNIX 1)
elseif(WIN32)
    set(CORRADE_TARGET_WINDOWS 1)

    if(WINDOWS_PHONE OR WINDOWS_STORE)
        set(CORRADE_TARGET_WINDOWS_RT 1)
    endif()
endif()

option(CORRADE_WITH_INTERCONNECT "Build Interconnect library" ON)
option(CORRADE_WITH_PLUGINMANAGER "Build PluginManager library" ON)
option(CORRADE_WITH_TESTSUITE "Build TestSuite library" ON)
cmake_dependent_option(CORRADE_WITH_UTILITY "Build Utility library" ON "NOT CORRADE_WITH_INTERCONNECT;NOT CORRADE_WITH_PLUGINMANAGER;NOT CORRADE_WITH_TESTSUITE" ON)
# If we're crosscompiling and the native executable was found above or the
# CORRADE_RC_EXECUTABLE was passed explicitly, we can skip building the in-tree
# version. See above for why it's additionally compared against Corrade::rc.
if(CORRADE_RC_EXECUTABLE AND NOT CORRADE_RC_EXECUTABLE STREQUAL Corrade::rc)
    if(NOT CORRADE_TARGET_WINDOWS_RT)
        option(CORRADE_WITH_RC "Build the corrade-rc utility" ON)
    else()
        if(CORRADE_WITH_RC)
            # Even if there would be an emulator that could run those, the
            # problem is that later on, find_program() will happily find the
            # cross-compiled version and treat it as native, likely because
            # nothing implicitly sets CMAKE_FIND_ROOT_PATH_MODE_PROGRAM to
            # NEVER when cross-compiling for this damn thing. And having to use
            # a toolchain to do that is as good as nothing, because when you
            # forget, nothing will notify you that it found the wrong exe until
            # your CI builds get silently stuck forever or until you're greeted
            # with an ugly-ass washed-out-blue dialog with sharp corners saying
            #
            #   +-----------------------------------------------------------+
            #   |                                                           |
            #   |  THIS APP CAN'T RUN ON YOUR PC                            |
            #   |                                                           |
            #   |  To find a version for your PC, check with the software   |
            #   |  publisher.                                               |
            #   |                                                 +-------+ |
            #   |                                                 | Close | |
            #   |                                                 +-------+ |
            #   +-----------------------------------------------------------+
            #
            # Amazing, eh??? So nope, corrade-rc is just not buildable here,
            # to keep sanity within reasonable bounds.
            message(FATAL_ERROR "The corrade-rc utility cannot be built when cross-compiling for the UWP platform. Set CORRADE_WITH_RC to OFF and supply a native version of it with CORRADE_RC_EXECUTABLE instead.")
        endif()
    endif()
# If no external CORRADE_RC_EXECUTABLE was found, corrade-rc is forcibly
# enabled as the Utility library depends on it.
else()
    cmake_dependent_option(CORRADE_WITH_RC "Build the corrade-rc utility" ON "NOT CORRADE_WITH_UTILITY" ON)
endif()
cmake_dependent_option(CORRADE_WITH_MAIN "Build Main library" ON "NOT CORRADE_WITH_TESTSUITE;NOT CORRADE_WITH_RC" ON)

option(CORRADE_BUILD_DEPRECATED "Include deprecated API in the build" ON)

option(CORRADE_BUILD_MULTITHREADED "Build in a way that makes it possible to safely use certain Corrade features simultaneously in multiple threads" ON)

# It's inconvenient to manually load all shared libs using Android / JNI,
# similarly on Emscripten, so there default to static.
if(CORRADE_TARGET_ANDROID OR CORRADE_TARGET_EMSCRIPTEN)
    set(OFF_EXCEPT_ANDROID_EMSCRIPTEN ON)
else()
    set(OFF_EXCEPT_ANDROID_EMSCRIPTEN OFF)
endif()
option(CORRADE_BUILD_STATIC "Build static libraries" ${OFF_EXCEPT_ANDROID_EMSCRIPTEN})
# Disable PIC on Emscripten by default (but still allow it to be enabled
# explicitly if one so desired). Currently causes linker errors related to
# __memory_base etc.: https://github.com/emscripten-core/emscripten/issues/8761
if(CORRADE_TARGET_EMSCRIPTEN)
    set(ON_EXCEPT_EMSCRIPTEN OFF)
else()
    set(ON_EXCEPT_EMSCRIPTEN ON)
endif()
cmake_dependent_option(CORRADE_BUILD_STATIC_PIC "Build static libraries with position-independent code" ${ON_EXCEPT_EMSCRIPTEN} "CORRADE_BUILD_STATIC" OFF)
cmake_dependent_option(CORRADE_BUILD_STATIC_UNIQUE_GLOBALS "Build static libraries with globals unique across shared libraries" ${ON_EXCEPT_EMSCRIPTEN} "CORRADE_BUILD_STATIC" OFF)
option(CORRADE_BUILD_TESTS "Build unit tests" OFF)

# Platform-specific options
if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_WINDOWS_RT OR CORRADE_TARGET_IOS OR CORRADE_TARGET_ANDROID)
    set(CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT 1)
endif()
if(CORRADE_TARGET_APPLE)
    option(CORRADE_TESTSUITE_TARGET_XCTEST "Build TestSuite tests compatible with Xcode XCTest" OFF)
endif()
if(CORRADE_TARGET_WINDOWS)
    if(CORRADE_TARGET_WINDOWS_RT)
        set(CORRADE_UTILITY_USE_ANSI_COLORS ON)
    else()
        option(CORRADE_UTILITY_USE_ANSI_COLORS "Use ANSI escape sequences for colored Debug output on Windows" OFF)
        # TODO is there some cmake_dependent_option() but for strings? I.e., to
        #   hide this if a static build isn't enabled? I don't want to repeat
        #   the complicated logic from inside cmake_dependent_option() just for
        #   a single option.
        set(CORRADE_BUILD_STATIC_UNIQUE_GLOBALS_DLL_NAME ""
            CACHE STRING "Name of a DLL in which to search for unique globals symbols if CORRADE_BUILD_STATIC_UNIQUE_GLOBALS is enabled")
    endif()
endif()

# Check if we can use IFUNC for CPU dispatch. Linux with glibc and Android with
# API 18+ has it, but e.g. Alpine Linux with musl doesn't, and on Android with
# API < 30 we don't get AT_HWCAP passed into the resolver and can't call
# getauxval() ourselves because it's too early at that point, which makes it
# pretty useless. Plus it also needs a certain binutils version and a capable
# compiler, so it's easiest to just verify the whole thing.
#
# The feature is supported on ELF platforms only, so general Linux/BSD but not
# Apple. TODO: however, without the NOT CORRADE_TARGET_APPLE, in some strange
# cases this seems to pass for iOS builds in a superproject setup (maybe a
# different compiler gets used for try_compile()?), figure out why:
# https://gist.github.com/hsdk123/3a5bde381e79327f754e35a82e12702a
if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE)
    include(CheckCXXSourceCompiles)
    check_cxx_source_compiles("\
int fooImplementation() { return 42; }
#if defined(__ANDROID_API__) && __ANDROID_API__ < 30
#error need Android API 30+ to have AT_HWCAP passed into the resolver
#endif
extern \"C\" int(*fooDispatcher())() {
    return fooImplementation;
}
int foo() __attribute__((ifunc(\"fooDispatcher\")));
int main() { return foo() - 42; }\
        " _CORRADE_CPU_CAN_USE_IFUNC)
    # In cases where ifunc is known to be broken, disable it by default --
    # users can still force it to be enabled, but the initial state should not
    # cause crashes or other strange behavior.
    if(_CORRADE_CPU_CAN_USE_IFUNC)
        set(_CORRADE_CPU_USE_IFUNC_DEFAULT ON)
        # On GCC 4.8, if --coverage or -fprofile-arcs is enabled, the ifunc
        # dispatchers cause a segfault. On Ubuntu 20.04 at least. Not the case
        # with GCC 5 there, not the case with GCC 4.8 on Arch. Can't find any
        # upstream bug report or commit that would be related to this.
        if(CMAKE_CXX_COMPILER_ID AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9" AND CMAKE_CXX_FLAGS MATCHES "(--coverage|-fprofile-arcs)")
            if(NOT DEFINED CORRADE_CPU_USE_IFUNC)
                message(WARNING "Disabling CORRADE_CPU_USE_IFUNC by default as it may crash when used together with --coverage on GCC 4.8.")
            endif()
            set(_CORRADE_CPU_USE_IFUNC_DEFAULT OFF)
        endif()

        # If sanitizers are enabled, call into the dispatch function crashes.
        # Upstream bugreport https://github.com/google/sanitizers/issues/342
        # suggests using __attribute__((no_sanitize_address)), but that doesn't
        # work / can't be used because it would mean marking basically
        # everything including the actual implementation that's being
        # dispatched to.
        #
        # Sanitizers can be also enabled any other way such as with
        # CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER or with per-target properties
        # which isn't easy to detect, so as a secondary measure there's a kill
        # switch in Corrade/configure.h that will fail with an error if a
        # sanitizer is detected and CORRADE_CPU_USE_IFUNC is enabled.
        #
        # While Clang and GCC use -fsanitize=whatever, MSVC allows also
        # /fsanitize=, so catch both.
        if(CMAKE_CXX_FLAGS MATCHES "[-/]fsanitize=")
            if(NOT DEFINED CORRADE_CPU_USE_IFUNC)
                message(WARNING "Disabling CORRADE_CPU_USE_IFUNC by default as it crashes when used together with sanitizers. See https://github.com/google/sanitizers/issues/342 for more information.")
            endif()
            set(_CORRADE_CPU_USE_IFUNC_DEFAULT OFF)
        endif()
    else()
        set(_CORRADE_CPU_USE_IFUNC_DEFAULT OFF)
    endif()
else()
    set(_CORRADE_CPU_CAN_USE_IFUNC OFF)
    set(_CORRADE_CPU_USE_IFUNC_DEFAULT OFF)
endif()
cmake_dependent_option(CORRADE_CPU_USE_IFUNC "Allow using GNU IFUNC for runtime CPU dispatch" ${_CORRADE_CPU_USE_IFUNC_DEFAULT} "_CORRADE_CPU_CAN_USE_IFUNC" OFF)

# Runtime CPU dispatch. Because going through a function pointer may have
# negative perf consequences, enable it by default only on platforms that have
# IFUNC, and thus can avoid the function pointer indirection.
option(CORRADE_BUILD_CPU_RUNTIME_DISPATCH "Build with runtime dispatch for CPU-dependent functionality" ${_CORRADE_CPU_CAN_USE_IFUNC})
# Enabled by default and independent of CORRADE_BUILD_CPU_RUNTIME_DISPATCH as
# correctness is important. It can however negatively affect performance
# compared to IFUNC or compile-time dispatch, so make it possible to disable
# this for benchmarks.
cmake_dependent_option(CORRADE_BUILD_TESTS_FORCE_CPU_POINTER_DISPATCH "Force pointer-based dispatch for unit tests to verify all variants of CPU-dependent functionality" ON "CORRADE_BUILD_TESTS" OFF)

# Pass -msimd128 to tests to make it possible to verify also the WASM SIMD
# functionality even if the actual library is built without. I thought it would
# be a no-brainer, however *actual* finalized SIMD128 requires final Clang 13
# (such version is reported since emsdk 2.0.13, but the actual final version
# containing all intrinsics is only in emsdk 2.0.18), and Node.js 15+, which
# contains V8 8.6:
#   https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V15.md#2020-10-20-version-1500-current-bethgriggs
# Final bitmask instructions were added in 8.5, Node.js 14 has 8.4:
#   https://github.com/v8/v8/commit/aa5bcc09bf7d5d77056e6033918d79c30aa3f49a
# However, even now, in the midst of 2022 wildfires, emsdk still bundles
# Node.js 14.18 and there's no hope of it being upgraded any time soon:
#   https://github.com/emscripten-core/emsdk/pull/877
#   https://github.com/emscripten-core/emsdk/issues/947
#   https://github.com/emscripten-core/emsdk/issues/1064
# So instead of having -msimd128 always, I only enable it implicitly if Node.js
# 15 is found, and then CORRADE_ENABLE_SIMD128 / CORRADE_TARGET_SIMD128 gets
# enabled only if there's Clang 13 but also Emscripten 2.0.18+. What a mess.
if(CORRADE_TARGET_EMSCRIPTEN)
    set(ENABLE_IF_NODEJS_15 OFF)
    if(CORRADE_BUILD_TESTS)
        find_package(NodeJs REQUIRED QUIET)
        if(NOT NodeJs_VERSION VERSION_LESS 15.0)
            set(ENABLE_IF_NODEJS_15 ON)
        elseif(NOT DEFINED CORRADE_BUILD_TESTS_FORCE_WASM_SIMD128)
            message(WARNING "Disabling CORRADE_BUILD_TESTS_FORCE_WASM_SIMD128 by default as finalized WebAssembly SIMD support is only since Node.js 15 but found version ${NodeJs_VERSION}")
        endif()
    endif()

    cmake_dependent_option(CORRADE_BUILD_TESTS_FORCE_WASM_SIMD128 "Force -msimd128 for unit tests to verify also WASM SIMD variants of CPU-dependent functionality" ${ENABLE_IF_NODEJS_15} "CORRADE_BUILD_TESTS_FORCE_CPU_POINTER_DISPATCH" OFF)
endif()

# Backwards compatibility for unprefixed CMake options. If the user isn't
# explicitly using prefixed options in the first run already, accept the
# unprefixed options, and remember this decision for subsequent runs
if(NOT DEFINED _CORRADE_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS)
    set(_CORRADE_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS ON CACHE INTERNAL "")
endif()
# If the user wasn't explicitly using prefixed options in the first run and the
# CORRADE_BUILD_DEPRECATED option is not currently disabled (which can get
# changed subsequently), accept the unprefixed options and print a warning if
# they're different from the prefixed ones.
if(_CORRADE_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS AND CORRADE_BUILD_DEPRECATED)
    # BUILD_STATIC_PIC and BUILD_STATIC_UNIQUE_GLOBALS need extra care, as they
    # should be implicitly set to ON if BUILD_STATIC is set. Doing this before
    # propagating the unprefixed options to avoid a false-positive warning
    # when e.g. CORRADE_BUILD_STATIC_PIC is implicitly ON but BUILD_STATIC_PIC
    # not yet.
    if(BUILD_STATIC)
        if(NOT CORRADE_TARGET_EMSCRIPTEN AND NOT DEFINED BUILD_STATIC_PIC)
            set(BUILD_STATIC_PIC ON)
        endif()
        if(NOT CORRADE_TARGET_EMSCRIPTEN AND NOT DEFINED BUILD_STATIC_UNIQUE_GLOBALS)
            set(BUILD_STATIC_UNIQUE_GLOBALS ON)
        endif()
    endif()

    set(_CORRADE_WARN_DEPRECATED_UNPREFIXED_OPTION )
    foreach(option ${_CORRADE_DEPRECATED_UNPREFIXED_OPTIONS})
        if(DEFINED ${option})
            # CMake has no comparison of boolean values (EQUAL returns false if
            # comparing ON and 1 or OFF and FALSE, STREQUAL also), so we have
            # to do it this way. Also warn only on the first encountered
            # variable so people can fix it, reconfigure and go to the next one
            # that warns.
            if((${option} AND NOT CORRADE_${option}) OR
               (NOT ${option} AND CORRADE_${option}) AND NOT _CORRADE_WARN_DEPRECATED_UNPREFIXED_OPTION)
                set(_CORRADE_WARN_DEPRECATED_UNPREFIXED_OPTION ${option})
            endif()
            set(CORRADE_${option} ${${option}})
            # If variables specified on the command line don't match any
            # options, they're kept in cache but set as UNINITIALIZED, meaning
            # they don't appear in cmake-gui or ccmake, so there's no way to
            # fix the warning apart from hand-enditing the CMakeCache.txt or
            # recreating the build dir. Update their cached type to be BOOL to
            # make them appear.
            set(${option} ${${option}} CACHE BOOL "Deprecated, use CORRADE_${option} instead" FORCE)
        endif()
    endforeach()

    if(_CORRADE_WARN_DEPRECATED_UNPREFIXED_OPTION)
        message(DEPRECATION "Unprefixed options such as ${_CORRADE_WARN_DEPRECATED_UNPREFIXED_OPTION} are deprecated, use CORRADE_${_CORRADE_WARN_DEPRECATED_UNPREFIXED_OPTION} instead. Delete the unprefixed variable from CMake cache or set both to the same value to silence this warning.")
    endif()
endif()

# Initialize variables, macros etc.
if(CORRADE_BUILD_TESTS)
    if(CORRADE_TARGET_IOS)
        set(CORRADE_TESTSUITE_BUNDLE_IDENTIFIER_PREFIX "cz.mosra.corrade")
    endif()
    enable_testing()

    # If CORRADE_TESTSUITE_TEST_TARGET is set, tests aren't built by default
    # (in the ALL target) but instead set as dependencies of a target named
    # after the value of CORRADE_TESTSUITE_TEST_TARGET. This is a copy of
    # what's done in corrade_add_test(), because we also build various test
    # libraries and plugins in addition to the test executables.
    if(CORRADE_TESTSUITE_TEST_TARGET)
        if(NOT TARGET ${CORRADE_TESTSUITE_TEST_TARGET})
            add_custom_target(${CORRADE_TESTSUITE_TEST_TARGET})
        endif()
        set(EXCLUDE_FROM_ALL_IF_TEST_TARGET EXCLUDE_FROM_ALL)
    endif()
endif()

# If we're in a CMake subproject, find_package(Corrade) will be looking for
# these, so supply their in-source location to cache
if(CORRADE_TESTSUITE_TARGET_XCTEST)
    set(CORRADE_TESTSUITE_XCTEST_RUNNER ${PROJECT_SOURCE_DIR}/src/Corrade/TestSuite/XCTestRunner.mm.in CACHE INTERNAL "" FORCE)
elseif(CORRADE_TARGET_ANDROID)
    set(CORRADE_TESTSUITE_ADB_RUNNER ${PROJECT_SOURCE_DIR}/src/Corrade/TestSuite/AdbRunner.sh CACHE INTERNAL "" FORCE)
elseif(CORRADE_TARGET_EMSCRIPTEN)
    set(CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER ${PROJECT_SOURCE_DIR}/src/Corrade/TestSuite/EmscriptenRunner.html.in CACHE INTERNAL "" FORCE)
endif()

# Detect and auto-enable compiler compatibility
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    # The oldest known and supported GCC currently in use is 4.8.5 on CentOS 7
    # (needed for some server installations)
    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.1")
        message(FATAL_ERROR "Corrade cannot be used with GCC < 4.8.1. Sorry.")
    endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.0")
        message(FATAL_ERROR "Corrade cannot be used with MSVC < 2015. Sorry.")
    elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.10")
        if(NOT CORRADE_MSVC2015_COMPATIBILITY)
            set(CORRADE_MSVC2015_COMPATIBILITY ON)
            message(WARNING "MSVC 2015 detected, automatically enabling CORRADE_MSVC2015_COMPATIBILITY. Note that some features may not be available with this compiler.")
        endif()
    elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.20")
        if(NOT CORRADE_MSVC2017_COMPATIBILITY)
            set(CORRADE_MSVC2017_COMPATIBILITY ON)
            message(WARNING "MSVC 2017 detected, automatically enabling CORRADE_MSVC2017_COMPATIBILITY. Note that some features may not be available with this compiler.")
        endif()
    # CORRADE_MSVC_COMPATIBILITY is enforced by CORRADE_MSVC2017_COMPATIBILITY
    # and CORRADE_MSVC2015_COMPATIBILITY below, but can be disabled for 2019+.
    elseif(_CORRADE_MSVC_COMPATIBILITY_NOT_DEFINED)
        set(CORRADE_MSVC_COMPATIBILITY ON CACHE BOOL "Enable compatibility mode for MSVC 2019+ without /permissive- set (might disable some features)" FORCE)
        message(WARNING "MSVC 2019+ detected, automatically enabling CORRADE_MSVC_COMPATIBILITY which may cause some features to not be available. You can disable this option if you pass /permissive- to the compiler to enable a standards-conforming mode.")
    endif()

    if(CORRADE_MSVC2017_COMPATIBILITY)
        set(CORRADE_MSVC_COMPATIBILITY ON)
    endif()

    if(CORRADE_MSVC2015_COMPATIBILITY)
        set(CORRADE_MSVC2017_COMPATIBILITY ON)
        set(CORRADE_MSVC_COMPATIBILITY ON)
    endif()
endif()

include(UseCorrade)

# Installation paths
include(CorradeLibSuffix)
set(CORRADE_BINARY_INSTALL_DIR bin)
set(CORRADE_LIBRARY_INSTALL_DIR lib${LIB_SUFFIX})
set(CORRADE_DATA_INSTALL_DIR share/corrade)
set(CORRADE_CMAKE_MODULE_INSTALL_DIR share/cmake/Corrade)
set(CORRADE_INCLUDE_INSTALL_DIR include/Corrade)
# Prefix the non-binary paths with the obsolete CORRADE_INCLUDE_INSTALL_PREFIX,
# if set
if(CORRADE_BUILD_DEPRECATED AND CORRADE_INCLUDE_INSTALL_PREFIX AND NOT CORRADE_INCLUDE_INSTALL_PREFIX STREQUAL ".")
    message(DEPRECATION "CORRADE_INCLUDE_INSTALL_PREFIX is obsolete as its primary use was for old Android NDK versions. Please switch to the NDK r19+ layout instead of using this variable and recreate your build directory to get rid of this warning.")
    set(CORRADE_DATA_INSTALL_DIR ${CORRADE_INCLUDE_INSTALL_PREFIX}/${CORRADE_DATA_INSTALL_DIR})
    set(CORRADE_CMAKE_MODULE_INSTALL_DIR ${CORRADE_INCLUDE_INSTALL_PREFIX}/${CORRADE_CMAKE_MODULE_INSTALL_DIR})
    set(CORRADE_INCLUDE_INSTALL_DIR ${CORRADE_INCLUDE_INSTALL_PREFIX}/${CORRADE_INCLUDE_INSTALL_DIR})
endif()

# Library version. CORRADE_VERSION_YEAR/MONTH is used in
# src/Corrade/CMakeLists.txt to generate the version.h header.
set(CORRADE_LIBRARY_VERSION 2.4)
set(CORRADE_LIBRARY_SOVERSION 2)
set(CORRADE_VERSION_YEAR 2020)
set(CORRADE_VERSION_MONTH 6)

# A single output location. After a decade of saying NO THIS IS A NON-SOLUTION
# TO A NON-PROBLEM I reconsidered my views and enabled this, because:
#
# - On Windows (which don't have RPATH), this makes test execution finally
#   possible without having to install all the stuff first (including the
#   test-only libs, which is ugh).
# - With CMake subprojects, this makes it finally possible to use dynamic
#   plugins directly from the build dir (again without installing anything) ---
#   all plugins are put into the same place, so PluginManager has a single
#   place to look into; and thanks to the dynamic libraries being there as
#   well, this location can be automagically detected as relative to
#   Utility::Path::libraryLocation().
# - Thanks to the $<CONFIG> being part of the output path, you are always sure
#   you never accidentally mix up debug/release libraries when switching
#   CMAKE_BUILD_TYPE in an existing build dir.
#
# The runtime location is set to CMAKE_BINARY_DIR and not PROJECT_BINARY_DIR
# because have one runtime location per CMake subproject would not solve much
# either. If the user already provides CMAKE_RUNTIME_OUTPUT_DIRECTORY (even
# empty), it's respected and nothing is being done.
#
# Explicitly using a generator expression to ensure plugins are added to e.g.
# <CONFIG>/lib/corrade/fs/ instead of lib/corrade/fs/<CONFIG>. Also adding this
# to cache, making superprojects pick that up implicitly as well, without
# forcing them to explicitly mirror this setting.
if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY AND NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY AND NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/bin CACHE PATH "" FORCE)
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/lib CACHE PATH "" FORCE)
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/lib CACHE PATH "" FORCE)
    # There should be no need for the "90% use case" user to adjust these, so
    # don't show them in the default view
    mark_as_advanced(
        CMAKE_RUNTIME_OUTPUT_DIRECTORY
        CMAKE_LIBRARY_OUTPUT_DIRECTORY
        CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
endif()

# Implicitly, corrade_add_test(), corrade_add_plugin() and other macros check
# for existence of Corrade::TestSuite, Corrade::PluginManager and other targets
# to avoid cryptic errors later during the build. However, when building
# Corrade itself, those may not be defined yet (for example Containers tests
# are added before the TestSuite subdirectory), however it will work correctly
# during the actual build and thus such errors would be wrong.
set(_CORRADE_USE_NO_TARGET_CHECKS 1)

add_subdirectory(modules)
add_subdirectory(src)

# Build snippets as part of testing. Unlike all other Test/ directories, this
# one isn't added with ${EXCLUDE_FROM_ALL_IF_TEST_TARGET} because the targets
# from there aren't referenced by CTest and thus it'd likely happen that they
# accidentally don't get added as a dependency to that target, causing them to
# be never built. Instead, each target there is handled separately, to minimize
# the chance of an accident.
if(CORRADE_BUILD_TESTS)
    add_subdirectory(doc/snippets)
endif()
